/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "Shared/DB/SqlPreparedStatement.h"
#include <algorithm>
#include "Shared/Log/Log.h"
#include "Shared/DB/SqlConnection.h"
#include "Shared/DB/Database.h"
#pragma warning(disable:4996)


namespace Shared {
namespace DB {

    SqlStmtParameters::SqlStmtParameters(std::uint32_t nParams)
    {
        if (nParams > 0)
        { _params.reserve(nParams); }
    }

    void SqlStmtParameters::reset(const SqlStatement& stmt)
    {
        _params.clear();
        if (stmt.arguments() > 0)
        { _params.reserve(stmt.arguments()); }
    }

    //////////////////////////////////////////////////////////////////////////
    SqlStatement& SqlStatement::operator=(const SqlStatement& index)
    {
        if (this != &index)
        {
            _index = index._index;
            _db = index._db;

            delete _params;
            _params = NULL;

            if (index._params)
            { _params = new SqlStmtParameters(*(index._params)); }
        }

        return *this;
    }

    bool SqlStatement::execute()
    {
        SqlStmtParameters* args = detach();
        if (args->boundParams() != arguments())
        {
            sharedLog.outDbError("SQL ERROR: wrong amount of parameters (%i instead of %i)", args->boundParams(), arguments());
            sharedLog.outDbError("SQL ERROR: statement: %s", _db->getStmtString(id()).c_str());
            SHARED_ASSERT(false);
            return false;
        }

        return _db->executeStmt(_index, args);
    }

    bool SqlStatement::directExecute()
    {
        SqlStmtParameters* args = detach();
        // verify amount of bound parameters
        if (args->boundParams() != arguments())
        {
            sharedLog.outDbError("SQL ERROR: wrong amount of parameters (%i instead of %i)", args->boundParams(), arguments());
            sharedLog.outDbError("SQL ERROR: statement: %s", _db->getStmtString(id()).c_str());
            SHARED_ASSERT(false);
            return false;
        }

        return _db->directExecuteStmt(_index, args);
    }

    //////////////////////////////////////////////////////////////////////////
    SqlPlainPreparedStatement::SqlPlainPreparedStatement(const std::string& fmt, SqlConnection& conn) 
        : SqlPreparedStatement(fmt, conn)
    {
        _prepared = true;
        _params = std::count(_format.begin(), _format.end(), '?');
        _query = strnicmp(_format.c_str(), "select", 6) == 0;
    }

    void SqlPlainPreparedStatement::bind(const SqlStmtParameters& holder)
    {
        // verify if we bound all needed input parameters
        if (_params != holder.boundParams())
        {
            SHARED_ASSERT(false);
            return;
        }

        // reset resulting plain SQL request
        _plainRequest = _format;
        size_t nLastPos = 0;

        SqlStmtParameters::ParameterContainer const& _args = holder.params();

        SqlStmtParameters::ParameterContainer::const_iterator iter_last = _args.end();
        for (SqlStmtParameters::ParameterContainer::const_iterator iter = _args.begin(); 
            iter != iter_last; ++iter)
        {
            // bind parameter
            const SqlStmtFieldData& data = (*iter);

            std::ostringstream fmt;
            dataToString(data, fmt);

            nLastPos = _plainRequest.find('?', nLastPos);
            if (nLastPos != std::string::npos)
            {
                std::string tmp = fmt.str();
                _plainRequest.replace(nLastPos, 1, tmp);
                nLastPos += tmp.length();
            }
        }
    }

    bool SqlPlainPreparedStatement::execute()
    {
        if (_plainRequest.empty())
        { return false; }

        return _conn.execute(_plainRequest.c_str());
    }

    void SqlPlainPreparedStatement::dataToString(const SqlStmtFieldData& data, std::ostringstream& fmt)
    {
        switch (data.type())
        {
        case FIELD_BOOL:    fmt << "'" << std::uint32_t(data.toBool()) << "'";     break;
        case FIELD_UI8:     fmt << "'" << std::uint32_t(data.toUint8()) << "'";    break;
        case FIELD_UI16:    fmt << "'" << std::uint32_t(data.toUint16()) << "'";   break;
        case FIELD_UI32:    fmt << "'" << data.toUint32() << "'";           break;
        case FIELD_UI64:    fmt << "'" << data.toUint64() << "'";           break;
        case FIELD_I8:      fmt << "'" << std::int32_t(data.toInt8()) << "'";      break;
        case FIELD_I16:     fmt << "'" << std::int32_t(data.toInt16()) << "'";     break;
        case FIELD_I32:     fmt << "'" << data.toInt32() << "'";            break;
        case FIELD_I64:     fmt << "'" << data.toInt64() << "'";            break;
        case FIELD_FLOAT:   fmt << "'" << data.toFloat() << "'";            break;
        case FIELD_DOUBLE:  fmt << "'" << data.toDouble() << "'";           break;
        case FIELD_STRING:
            {
                std::string tmp = data.toStr();
                _conn.db().escapeString(tmp);
                fmt << "'" << tmp << "'";
                break;
            }
        case FIELD_NONE:                                                    break;
        }
    }

}}